#import "XTAction.h"
#import "XText.h"
#import "ErrorStream.h"
#import <appkit/Application.h>
#import <stdio.h>
#import <string.h>
#import <appkit/NXCType.h>



@implementation XTAction

static id undefined_action = 0;

+ undefinedAction
{
	if (!undefined_action)
		undefined_action = [[XTAction allocFromZone:[NXApp zone]] init];
	return undefined_action;
}

- applyTo:xtext event:(NXEvent *)event
{
	[xtext unboundKey];
	return self;
}

@end

@implementation XTMsg0Action

- initSel:(SEL)sel
{
	[super init];
	action_sel = sel;
	return self;
}

- applyTo:xtext event:(NXEvent *)event
{
	return [xtext perform:action_sel];
}

@end

@implementation XTMsg1Action

- initSel:(SEL)sel arg:(int)arg
{
	[super init];
	action_sel = sel;
	action_arg = arg;
	return self;
}

- applyTo: xtext event:(NXEvent *)event
{
	return [xtext perform:action_sel with:(id)action_arg];
}

@end

@implementation XTMsg2Action

- initSel:(SEL)sel arg:(int)arg1 arg:(int)arg2
{
	[super init];
	action_sel = sel;
	action_arg1 = arg1;
	action_arg2 = arg2;
	return self;
}

- applyTo: xtext event:(NXEvent *)event
{
	return [xtext perform:action_sel
					with:(id)action_arg1 with:(id)action_arg2];
}

@end

@implementation XTDispatchAction

- init
{
	charCode k;

	[super init];

	for (k=0; k<CHAR_CODES; ++k) actions[k] = nil;
	
	return self;
}

// provided for backward compatability
- initBase:(const char *)base estream:errs
{
	charCode k;

	[super init];
	for (k=0; k<CHAR_CODES; ++k) actions[k] = nil;
	if (!strcmp(base,"none")) {}

//	else if (!strcmp(base,"other_base"))
//		initbase_other_base(actions)

	else {}
	return self;
}


// a convenience method to loading keybindings
- loadFromFile:(char *)fullName estream:errs
{
	FILE *fp;
	int i;
	char line[256];

	fp = fopen(fullName, "r");
	
	if(!fp){
		char msg[100];
		sprintf(msg,"Cannot read %s", fullName);
		[(errs ? errs : [ErrorStream default]) report:msg];
		return self;
	}
	
    while(fgets(line, 256, fp) != NULL){
		if(line[0] == '#' ){} // a comment
		else{
			for(i=0;i<strlen(line);i++){
				if(line[i] == '\n' || line[i] == '\t') line[i] = ' ';
			}
			[self addBindings:line estream:errs];
		}
	}
				
	return self;
}

- bindKey:(charCode)key toAction:action estream:errs
{
	if ((key < 0) || (key >= CHAR_CODES)) {
		char msg[40];
		sprintf(msg, "Invalid key code: %d", key);
		[(errs ? errs : [ErrorStream default]) report:msg];
	} else
		actions[key] = action;
	return self;
}

- applyTo:xtext event:(NXEvent *)event
{
	charCode k = event->data.key.charCode << NUM_MASKS;
	id action;

	if ((k >= 0) && (k < CHAR_CODES)) {
        if (event->flags & NX_ALPHASHIFTMASK)	k += 1;
		if (event->flags & NX_SHIFTMASK)      	k += 2;
		if (event->flags & NX_CONTROLMASK)    	k += 4;
		if (event->flags & NX_ALTERNATEMASK)  	k += 8;
		if (event->flags & NX_COMMANDMASK)    	k += 16;
		if (event->flags & NX_NUMERICPADMASK) 	k += 32;
		if (event->flags & NX_HELPMASK)       	k += 64;

		action = actions[k];
		if (action) return [action applyTo:xtext event:event];
        
		/* also try action without the caps lock bit on */
		if(event->flags & NX_ALPHASHIFTMASK){
			k-=1;
			action = actions[k];
			if (action) return [action applyTo:xtext event:event];
		}
	}
	return nil;
}

@end

@implementation XTEventMsgAction

- initSel:(SEL)sel
{
	[super init];
	action_sel = sel;
	return self;
}

- applyTo:xtext event:(NXEvent *)event
{
	return [xtext perform:action_sel with:(id)event];
}

@end

@implementation XTSeqAction

- initLength:(int)len actions:(XTAction **)acts
{
	[super init];
	length = len;
	actions = acts;
	return self;
}

- applyTo:xtext event:(NXEvent *)event
{
	int i;

	for (i=0; i<length; ++i)
		[actions[i] applyTo:xtext event:event];
	return self;
}

@end
